java线程状态和线程调度
文章目录
- java线程同步
- 线程安全
java线程同步
多线程共享数据可能会带来数据不安全的问题。可以给线程进行加锁的方式来解决类似问题。
第一种方法是同步方法
使用synchronized修饰的方法控制对类成员变量的访问
访问修饰符 synchronized 返回类型 方法名称(参数列表){.....}
或者是
synchronized 访问修饰符 返回类型 方法名称(参数列表){......}
synchronized给进程上了一把锁。
package Threads;
//模拟用户网络购票-->使用同步方法解决线程带来的数据不安全的问题
public class TicketThread1 implements Runnable {
private int ticket = 10;//记录车票总数
private int num = 0;//记录用户抢到了第几张票
private boolean flag = false;//代表票卖完了
//调用用户抢票的方法
@Override
public void run() {
while(!flag){
sale();
}
}
public synchronized void sale(){
if (ticket <= 0) {
flag = true;
return;
}
//有余票,则抢票
//修改车票数:总票数在抢完票后减一张
ticket--;
//用户抢完票后,票数加一
num++;
//模拟网络延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//显示出票反馈给用户
System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "张票,剩余" + ticket + "张票");
}
}
package Test;
import Threads.TicketThread1;
public class TicketThread1Test {
public static void main(String[] args) {
Runnable runnable = new TicketThread1();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
Thread t3 = new Thread(runnable);
System.out.println("各方开始抢票");
t1.start();
t2.start();
t3.start();
}
}
还可以同步代码块:
使用synchronized关键字修饰的代码块
synchronized(syncObject){
//需要同步的代码块
}
syncObject是需要同步的对象,通常是this
package Threads;
public class TicketThread2 implements Runnable {
private int ticket = 10;//记录车票总数
private int num = 0;//记录用户抢到了第几张票
//用户抢票
@Override
public void run() {
while (true) {
synchronized (this){
if (ticket <= 0) {
break;
}
//有余票,则抢票
//修改车票数:总票数在抢完票后减一张
ticket--;
//用户抢完票后,票数加一
num++;
//模拟网络延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//显示出票反馈给用户
System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "张票,剩余" + ticket + "张票");
}
}
}
}
package Test;
import Threads.TicketThread2;
public class TicketThread2Test {
public static void main(String[] args) {
Runnable runnable = new TicketThread2();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
Thread t3 = new Thread(runnable);
System.out.println("各方开始抢票");
t1.start();
t2.start();
t3.start();
}
}
多个并发线程访问同一资源的同步代码块时:
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码。
- 限制黄牛党抢到一张票
package Threads;
//模拟用户网络购票-->使用同步方法解决线程带来的数据不安全的问题
public class TicketThread3 implements Runnable {
private int ticket = 10;//记录车票总数
private int num = 0;//记录用户抢到了第几张票
private boolean flag = false;//(1)代表票卖完了(2)线程是否结束 flag:true线程结束
//调用用户抢票的方法
@Override
public void run() {
while(!flag){
sale();
}
}
public synchronized void sale(){
if (ticket <= 0) {
flag = true;
return;
}
//有余票,则抢票
//修改车票数:总票数在抢完票后减一张
ticket--;
//用户抢完票后,票数加一
num++;
//模拟网络延迟
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//显示出票反馈给用户
System.out.println(Thread.currentThread().getName() + "抢到了第" + num + "张票,剩余" + ticket + "张票");
//判断当前的线程是否是黄牛党,如果是,线程结束,保证买一张票
if(Thread.currentThread().getName().equals("黄牛党")){
flag = true;
}else{
flag = false;
}
}
}
package Test;
import Threads.TicketThread3;
public class TicketThread3Test {
public static void main(String[] args) {
Runnable runnable = new TicketThread3();
Thread t1 = new Thread(runnable,"桃票票");
Thread t2 = new Thread(runnable,"快手张");
Thread t3 = new Thread(runnable,"黄牛党");
System.out.println("各方开始抢票");
t1.start();
t2.start();
t3.start();
}
}
- 多个人参加1000米接力跑,每人跑100米,换下一个选手,每跑10米显示信息
package Threads;
//模拟一个选手接力跑的过程
public class RunThread implements Runnable {
//多个选手共同跑1000米
private int meters = 1000;
//实现一个选手拿到接力棒跑100米的过程
@Override
public void run() {
while (true) {
//同步代码块保证在跑选手只有一个人
synchronized (this) {
if (meters < 100) {
break;
}
//一个人拿到接力棒跑完100米
System.out.println(Thread.currentThread().getName() + "拿到了接力棒");
for (int i = 10; i <= 100; i += 10) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
}
//剩余路途减少100米
meters -= 100;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package Test;
import Threads.RunThread;
public class RunThreadTest {
public static void main(String[] args) {
Runnable runnable = new RunThread();
Thread person1 = new Thread(runnable, "选手张");
Thread person2 = new Thread(runnable, "选手李");
Thread person3 = new Thread(runnable, "选手王");
Thread person4 = new Thread(runnable, "选手周");
Thread person5 = new Thread(runnable, "选手钱");
person1.start();
person2.start();
person3.start();
person4.start();
person5.start();
}
}
线程安全
ArrayList类的add()方法是非同步方法,当多个线程向同一个ArrayList对象添加数据时,可能会出现数据不一致的问题,所以ArrayList是非线程安全的类型
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value),
* which helps when add(E) is called in a C1-compiled loop.
*/
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
Hashtable实现了Map接口,Hashtable继承了Dictionary类,线程安全但是效率低,键和值不许为null
HashMap实现了Map接口,继承AbstractMap类,非线程安全但是效率高,键和值都允许为null
StringBuffer线程安全 StringBuilder非线程安全。
线程安全需要的是方法要进行同步,效率比较低,适合多线程并发共享资源。
非线程安全是在方法不同步,效率比较高,适用于单线程。
在编码时为了达到安全性和效率的平衡,可以根据实际的场景来进行选择。
java网络编程