多线程、同步代码块、Lock锁、线程池

33 篇文章 0 订阅
30 篇文章 0 订阅

多线程

进程:正在运行的程序
线程

在一个进程内部可以执行多个任务,而这每一个任务我们就可以看成是一个线程。
是程序使用CPU的基本单位。

多线程:多线程的作用不是提高执行速度,而是为了提高应用程序的使用率

例:对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,
且切换速度很快,所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。

并行和并发:

并发 : 指应用能够交替执行不同的任务,快速切换;
(物理上同时发生,指在某一个时间点同时运行多个程序。)

并行 : 指应用能够同时执行不同的任务;
(逻辑上同时发生,指在某一个时间内同时运行多个程序)
例:吃饭的时候可以边吃饭边听音乐, 这两件事情可以同时执行

Java程序运行原理:

1、Java程序运行原理
	Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
	该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
	所以 main方法运行在主线程中。
2、JVM的启动是多线程的,因为JVM启动至少启动了垃圾回收线程和主线程。

Thread类常用方法:

1、Thread类的基本获取和设置方法
	public final String getName()//获取线程名称
	public final void setName(String name)//设置线程名称
	其实通过构造方法也可以给线程起名字
2、如何设置和获取线程优先级
	public final int getPriority() //获取线程的优先级
	public final void setPriority(int newPriority)//设置线程的优先级	
3、线程休眠:
	public static void sleep(long millis) 线程休眠
4、加入线程:
	public final void join()/// 等待该线程执行完毕了以后,其他线程才能再次执行
	注意事项: 在线程启动之后,在调用方法
5、礼让线程:
	public static void yield():	暂停当前正在执行的线程对象,并执行其他线程。
6、守护线程:
	public final void setDaemon(boolean on):
	//将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 
	该方法必须在启动线程前调用。 
7、中断线程
	public final void stop():		停止线程的运行
	public void interrupt():		
	当线程调用wait(),sleep(long time)方法的时候处于阻塞状态,
	可以通过这个方法清除阻塞

多线程程序实现的方式:
一、继承Thread类

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread("吕洞宾");
        th1.start();
        Thread.sleep(1000);
        th1.interrupt();//打断线程的阻塞状态,让线程继续运行
    }
}
class MyThread extends Thread {
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() { 
        try {
            Thread.sleep(5000); 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {

            System.out.println(Thread.currentThread().getName() + "===" + i);

        }
    }
}

二、实现Runnable接口:

public class test2 {
    public static void main(String[] args) {
        Thread th = new Thread(myRunable,"萧炎");
        th.start();
        Thread th2 = new Thread(myRunable,"萧薰儿");
        th2.start();
    }
}
class MyRunable implements Runnable { 
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "==" + i);
        }
    }
}

三、实现 Callable 接口:

 //实现 Callable 接口。相较于实现 Runnable 接口的方式,
   方法可以有返回值,并且可以抛出异常。
public class test3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable);
        Thread th = new Thread(integerFutureTask);
        th.start();
        
        Integer integer = integerFutureTask.get(); 
        //获取线程执行完之后,返回的结果
        System.out.println(integer);
    }
}
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"啦啦啦啦啦");
        return 100;
    }
}

线程安全问题:

判断一个多线程应用程序是否有问题的标准:
1、是否是多线程环境
2、是否存在共享数据
3、是否存在多条语句同时操作共享数据

同步代码块:

1、格式:
	synchronized(对象){
		需要同步的代码;
	}
2、优缺点:
	好处:同步的出现解决了多线程的安全问题。
    弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,
         无形中会降低程序的运行效率。
	同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能
3、java的内置锁:
  每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。
  线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。
  获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

同步方法: 就是把同步关键字加到方法上

同步代码块的锁对象: 	任意一个对象
同步方法的锁对象:   	是this
静态同步方法的锁对象:就是当前类对应的字节码文件对象

Lock锁:

常见方法:
    public void lock()   获取锁
	public void unlock()  释放锁
例:完成多个窗口共卖100张票的程序,并测试
public class test1 {
    public static void main(String[] args) {
        
        CellRunable cellRunable = new CellRunable();
        Thread th1 = new Thread(cellRunable);
        Thread th2 = new Thread(cellRunable);
        Thread th3 = new Thread(cellRunable);
        th1.setName("窗口1");
        th2.setName("窗口2");
        th3.setName("窗口3");
        th1.start();
        th2.start();
        th3.start();
    }
}
class CellRunable implements Runnable {
    static int piao = 100;
    static Object obj = new Object();
    static Lock lock = new ReentrantLock();
    @Override
    public void run() {

        while (true) {
            lock.lock(); //加锁
            if (piao > 0) {

                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在售卖 " + (piao--) + " 张票");
            }
            lock.unlock(); //释放锁
        }
    }

}

死锁问题:

1、如果出现了同步嵌套,就容易产生死锁问题;
   是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象

例:  //两个或者两个以上的线程, 在抢占CPU的执行权的时候, 都处于等待状态
public class test2 {
    public static void main(String[] args) {
        
        MyThread th1 = new MyThread(true);
        MyThread th2 = new MyThread(false);
        th1.start();
        th2.start();
    }
}
interface LockeInterface {
    //定义两把锁
    public static final  Object objA=new Object();
    public static final  Object objB=new Object();
}
class MyThread extends Thread {
    boolean b;

    public MyThread(boolean b) {
        this.b = b;
    }

    @Override
    public void run() {
        if (b) {
            synchronized (LockeInterface.objA) {
                System.out.println("true objA 进来了");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (LockeInterface.objB) {
                    System.out.println("true objB 进来了");
                }
            }
        } else {
            synchronized (LockeInterface.objB) {
                System.out.println("false objB 进来了");
                synchronized (LockeInterface.objA) {
                    System.out.println("false objA 进来了");
                }
            }
        }
    }
}

线程的状态转换图:
在这里插入图片描述

   新建:线程被创建出来
   就绪:具有CPU的执行资格,但是不具有CPU的执行权
   运行:具有CPU的执行资格,也具有CPU的执行权
   阻塞:不具有CPU的执行资格,也不具有CPU的执行权
   死亡:不具有CPU的执行资格,也不具有CPU的执行权

线程池:
线程池可以很好的提高性能,线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用

JDK5新增了一个Executors工厂类来产生线程池,有以下几个方法:
	public static ExecutorService newCachedThreadPool():			
		根据任务的数量来创建线程对应的线程个数	
	public static ExecutorService newFixedThreadPool(int nThreads):	
		固定初始化几个线程
	public static ExecutorService newSingleThreadExecutor():			
		初始化一个线程的线程池,这些方法的返回值是ExecutorService对象,
		该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。
		它提供了如下方法:
				Future<?> submit(Runnable task)
				<T> Future<T> submit(Callable<T> task)
			使用步骤:
				创建线程池对象      
				创建Runnable实例   
				提交Runnable实例   
				关闭线程池

匿名内部类的方式实现多线程程序:

例:
public class test {
    public static void main(String[] args) {
     
        //方式1
        new Thread() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        }.start();

        //方式2
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        }).start();
    }
}

定时器:
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。

通过Timer和TimerTask类来实现定义调度的功能
Timer:
		public Timer()
		public void schedule(TimerTask task, long delay):	
		public void schedule(TimerTask task,long delay,long period);
		public void schedule(TimerTask task,  Date time):
		public void schedule(TimerTask task,  Date firstTime, long period):
TimerTask:
		public abstract void run()
		public boolean cancel()
例:
public class MyTest {
    public static void main(String[] args) throws ParseException {
      
        Timer timer = new Timer();      
        MyTimerTask myTimerTask = new MyTimerTask(timer);
        
        String str = "2019-05-17 19:00:00";
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
        timer.schedule(myTimerTask, date);
        // timer.cancel();取消定时器

    }
}
class MyTimerTask extends TimerTask {
    Timer timer;
    public MyTimerTask(Timer timer) {
        this.timer=timer;

    }
    @Override
    public void run() {
        System.out.println("叮叮叮,起床啦");
        
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值