java多线程1

创建多线程

方式一

package com.pan;

class Window extends Thread{
    private static int ticket = 100;
    public void run(){
        while(true){
            if(ticket > 0){
                System.out.println(this.getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }
            else{
                break;
            }
        }
    }
}
public class ThreadMethodTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

方式二

class Window implements Runnable{
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            if(i % 2 == 0){
            System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}
public class ThreadMethodTest {
    public static void main(String[] args) {
        Window w = new Window();
        Thread thread = new Thread(w);
        thread.start();

        Thread t2 = new Thread(w);
        t2.start();
    }
}

Thread中的常用方法

  • start():启动当前线程(调用当前线程的run())
  • run()
  • currentThread():静态方法,返回执行当前代码的线程
  • getName():获取当前线程的名字
  • setName():设置当前线程的名字,也可以通过构造方法的方式自定义线程的名字,例如:
class HelloThread extends Thread{
    @Override
    public void run(){

    }
    public HelloThread(String name){
        super(name);
    }
}
public class ThreadMethodTest {
    public static void main(String[] args) {
        HelloThread h1 = new HelloThread("T1");
        System.out.println(h1.getName());//T1
    }
}
  • yield():释放当前cpu的执行权
  • join():在线程A中调用b中的join(),此时A线程就进入阻塞状态,直到线程B执行结束之后,线程A才结束阻塞状态。
  • sleep(Long millitime):使当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态
  • isAlive():判断当前线程是否存活

线程的优先级

  • MAX_PRIORITY:10
  • MIN_PRIORITY:1
  • NORM_PRIORITY:5
    2.如何获取和设置当前线程的优先级
  • getPriority():获取线程的优先级
  • setPriority(int p):设置线程的优先级
    说明:高优先级的线程占有cpu执行权的概率高于低优先级的线程。

线程的生命周期

在这里插入图片描述

线程安全

例子:创建三个窗口卖票,总票数为100张。

  • 问题:买票过程中,出现了重票、错票–》出现了线程安全问题
  • 问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
  • 解决方案:当一个线程A在操作ticket时,其他线程不能参与进来,直到线程A操作完成ticket时,其他线程才可以开始操作ticket,这种情况即使线程A出现了阻塞,也不能改变。
  • 在Java中,通过同步机制来解决线程安全问题。
  • 方式一:同步代码块
    synchronized(同步监视器){
    //需要被同步的代码
    }
    说明:1.操作共享数据(多个线程共同操作的变量)的代码,即为需要被同步的代码
    2.同步监视器俗称锁,任何一个类的对象都可以充当锁(多个线程必须共用一把锁)
    • 使用同步代码块解决实现Runnable接口创建多线程的方式的线程安全问题
      注意:可以考虑使用this充当同步监视器
class Window1 implements Runnable{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while(true) {
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
  • 使用同步代码块解决继承Thread类的方式的线程安全问题
    注意:慎用this充当同步监视器,可以考虑使用当前类充当同步监视器。
class Window extends Thread{
    private static int ticket = 100;
    static Object obj = new Object();
    public void run(){
        while(true) {
            synchronized (obj)/*synchronized (Window.class)*/ {
                //此时不能用this,因为this代表t1,t2,t3三个对象
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(this.getName() + ":卖票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
public class WindowTest2 {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

以上两个的输出:

窗口1:卖票,票号为:100
窗口1:卖票,票号为:99
窗口1:卖票,票号为:98
窗口1:卖票,票号为:97
窗口1:卖票,票号为:96
窗口1:卖票,票号为:95
窗口1:卖票,票号为:94
窗口1:卖票,票号为:93
窗口1:卖票,票号为:92
窗口1:卖票,票号为:91
窗口1:卖票,票号为:90
窗口1:卖票,票号为:89
窗口1:卖票,票号为:88
窗口1:卖票,票号为:87
窗口3:卖票,票号为:86
窗口3:卖票,票号为:85
窗口3:卖票,票号为:84
窗口3:卖票,票号为:83
窗口3:卖票,票号为:82
窗口3:卖票,票号为:81
窗口3:卖票,票号为:80
窗口3:卖票,票号为:79
窗口3:卖票,票号为:78
窗口3:卖票,票号为:77
窗口3:卖票,票号为:76
窗口3:卖票,票号为:75
窗口3:卖票,票号为:74
窗口2:卖票,票号为:73
窗口2:卖票,票号为:72
窗口2:卖票,票号为:71
窗口2:卖票,票号为:70
窗口2:卖票,票号为:69
窗口2:卖票,票号为:68
窗口2:卖票,票号为:67
窗口2:卖票,票号为:66
窗口2:卖票,票号为:65
窗口2:卖票,票号为:64
窗口2:卖票,票号为:63
窗口2:卖票,票号为:62
窗口2:卖票,票号为:61
窗口2:卖票,票号为:60
窗口2:卖票,票号为:59
窗口2:卖票,票号为:58
窗口2:卖票,票号为:57
窗口2:卖票,票号为:56
窗口2:卖票,票号为:55
窗口2:卖票,票号为:54
窗口2:卖票,票号为:53
窗口2:卖票,票号为:52
窗口2:卖票,票号为:51
窗口2:卖票,票号为:50
窗口2:卖票,票号为:49
窗口2:卖票,票号为:48
窗口2:卖票,票号为:47
窗口2:卖票,票号为:46
窗口2:卖票,票号为:45
窗口2:卖票,票号为:44
窗口2:卖票,票号为:43
窗口2:卖票,票号为:42
窗口2:卖票,票号为:41
窗口2:卖票,票号为:40
窗口2:卖票,票号为:39
窗口2:卖票,票号为:38
窗口2:卖票,票号为:37
窗口2:卖票,票号为:36
窗口2:卖票,票号为:35
窗口2:卖票,票号为:34
窗口2:卖票,票号为:33
窗口2:卖票,票号为:32
窗口2:卖票,票号为:31
窗口2:卖票,票号为:30
窗口2:卖票,票号为:29
窗口2:卖票,票号为:28
窗口2:卖票,票号为:27
窗口2:卖票,票号为:26
窗口2:卖票,票号为:25
窗口2:卖票,票号为:24
窗口2:卖票,票号为:23
窗口2:卖票,票号为:22
窗口2:卖票,票号为:21
窗口2:卖票,票号为:20
窗口2:卖票,票号为:19
窗口2:卖票,票号为:18
窗口2:卖票,票号为:17
窗口2:卖票,票号为:16
窗口2:卖票,票号为:15
窗口2:卖票,票号为:14
窗口2:卖票,票号为:13
窗口2:卖票,票号为:12
窗口2:卖票,票号为:11
窗口2:卖票,票号为:10
窗口2:卖票,票号为:9
窗口2:卖票,票号为:8
窗口2:卖票,票号为:7
窗口2:卖票,票号为:6
窗口2:卖票,票号为:5
窗口2:卖票,票号为:4
窗口2:卖票,票号为:3
窗口2:卖票,票号为:2
窗口2:卖票,票号为:1

Process finished with exit code 0

同步的方式解决了线程的安全问题,局限性是操作同步代码时,只能有一个线程参与,其他线程等待,相当于一个单线程的过程,效率低。

  • 方式二:同步方法
    如果操作共享数据的代码完整的声明在一个方法中,我们可以将此方法声明为同步的。
  • 使用同步方法解决实现Runnable接口的方式的线程安全问题,如下:
class Window1 implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while(true) {
                show();
        }
    }
    public synchronized void  show(){//默认同步监视器为:this
        if (ticket > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
            ticket--;
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window1 w = new Window1();

        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}
  • 使用同步方法处理继承Thread类的线程安全问题,如下:
class Window extends Thread{
    private static int ticket = 100;
    static Object obj = new Object();
    public void run(){
        while(true) {
            show();
        }
    }
    public static synchronized void show(){//同步监视器:Window.class
        /*public  synchronized void show(){*///同步监视器:t1,t2,t3,这种解决方式是错误的
        if (ticket > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
            ticket--;
        }
        else{
            return;
        }
    }
}
public class WindowTest2 {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
  • 方式三:使用Lock锁方式
package com.hai;
import java.util.concurrent.locks.ReentrantLock;
/*
* 解决线程安全问题的方式之一:Lock锁
* */
class Window implements Runnable{
    private int ticket = 100;
    //1.实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try {
                //2.调用锁定方法lock()
                lock.lock();
                if(ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            } finally {
                //3.调用解锁方法:unlock
                lock.unlock();
            }
        }
    }
}
public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
//synchronized与Lock的异同:
//相同:二者都可以解决线程安全问题
//不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器,而Lock需要手动的启动同步(lock()),同时也需要手动结束同步(unlock())

关于同步方法:

  • 同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
  • 非静态的同步方法同步监视器是:this
  • 静态的同步方法,同步监视器是:当前类本身

使用同步机制将单例模式中的懒汉式改写为线程安全

package com.pan;
/*
* 使用同步机制将单例模式中的懒汉式改写为线程安全的
*
* */

class Bank{
    private Bank(){
    }
    private static Bank instance = null;
    //方式一:
//    public static synchronized Bank getInstance(){
//        if(instance == null){
//            instance = new Bank();
//            return instance;
//        }
//        return instance;
//    }
    //方式二:
    public static Bank getInstance() {
        if (instance == null) {
            synchronized (Bank.class){
                if(instance == null){
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}
public class BankTest {
}

死锁

one:

public class TheadTest {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            @Override
            public void run(){
                synchronized (s1){
                    s1.append("a");
                    s1.append("1");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                synchronized (s2){
                    s1.append("b");
                    s2.append("2");
                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s1.append("3");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                synchronized (s1){
                    s1.append("d");
                    s2.append("4");
                    System.out.println(s1);
                    System.out.println(s2);
                }
            }
        });
    }
}

two:

class A{
    public synchronized void foo(B b){//A类的对象:a
        System.out.println("当前线程名:" + Thread.currentThread().getName() + "进入A实例的foo方法");
        try{
            Thread.sleep(200);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("当前线程名:" + Thread.currentThread().getName() + "企图调用B实例的last方法");
        b.last();
    }
    public synchronized void last(){
        System.out.println("进入A类的last方法内部");
    }
}
class B{
    public synchronized void bar(A a){
        System.out.println("当前线程名:" + Thread.currentThread().getName() + "进入了B实例的bar方法");
        try{
            Thread.sleep(200);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        a.last();
    }
    public synchronized void last(){
        System.out.println("进入B类的last方法内部");
    }
}
public class DeadLock implements Runnable {
    A a = new A();
    B b = new B();
    public void init(){
        Thread.currentThread().setName("主线程");
        a.foo(b);
        System.out.println("进入主线程之后");
    }
    public void run(){
        Thread.currentThread().setName("副线程");
        b.bar(a);
        System.out.println("进入副线程之后");
    }

    public static void main(String[] args) {
        DeadLock d1 = new DeadLock();
        new Thread(d1).start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pk5515

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值