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网络编程