继承Thread
package task.kkb.tatk;
public class MyThread extends Thread{
/**
* run=方法就是线程要执行的方法
*/
@Override
public void run() {
//这里的代码 就是一条新的执行路径
//这个执行路径的处罚方法不是调用run方法.而是通过thread对象的start()来启动任务
for (int i=0 ;i<10;i++) {
System.out.println("锄禾日当午"+i);
}
}
}
package task.kkb.tatk;
public class Demo {
public static void main(String[] args) {
MyThread m =new MyThread();
m.start();
for (int i =0;i<10;i++){
System.out.println("汗滴禾下土"+i);
}
}
}
匿名
new Thread(){
@Override
public void run() {
for (int i =0;i<10;i++){
System.out.println("一二三四五"+i);
}
}
}.start();
实现Runnable
package task.kkb.tatk;
public class MyRunnable implements Runnable{
@Override
public void run() {
//线程任务
for (int i =0;i<10;i++){
System.out.println("床前明月光"+i);
}
}
}
package task.kkb.tatk;
public class Demo {
/**
* 多线程技术
* 实现Runnable 与 继承Thread 相比有如下优势
* 1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务情况
* 2. 可以避免单继承带来的局限性
* 3. 任务与线程本身是分离的,提高了程序的健壮性
* 4. 后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
* @param args
*/
public static void main(String[] args) {
//实现Runnable
//创建一个任务对象
MyRunnable r = new MyRunnable();
//2. 创建一个线程,并为其分配一个任务
Thread t = new Thread(r);
//3. 执行这个线程
t.start();
for (int i =0;i<10;i++){
System.out.println("易时代打死你个"+i);
}
}
}
设置和获取线程名称
package task.kkb.tatk;
public class Demo {
/**
* 多线程技术
* 实现Runnable 与 继承Thread 相比有如下优势
* 1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务情况
* 2. 可以避免单继承带来的局限性
* 3. 任务与线程本身是分离的,提高了程序的健壮性
* 4. 后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
* @param args
*/
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
//不设置名称,默认名称Thread-0
new Thread(new MyRunnable()).start();
//也可通过new个新对象设置对象名称
Thread t = new Thread(new MyRunnable());
t.setName("123");
t.start();
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
//currentThread获取当前正在执行线程对象
System.out.println(Thread.currentThread().getName());
}
}
}
线程休眠
package task.kkb.tatk;
public class Demosleep {
public static void main(String[] args) throws InterruptedException {
//线程休眠 sleep
for (int i =0;i<10;i++){
System.out.println(i);
//1秒执行一次(1000毫秒等于一秒)
Thread.sleep(1000);
}
}
}
线程的中断
package task.kkb.tatk;
public class Demo {
/**
* 多线程技术
* 实现Runnable 与 继承Thread 相比有如下优势
* 1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务情况
* 2. 可以避免单继承带来的局限性
* 3. 任务与线程本身是分离的,提高了程序的健壮性
* 4. 后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
* @param args
*/
public static void main(String[] args) {
Thread ti = new Thread(new MyRunnable());
ti.start();
for (int i =1;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//给线程t1添加中断标记
ti.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i =1;i<11;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("线程中断了,我们结束");
//return结束
return;
}
}
}
}
}
守护线程
package task.kkb.tatk;
public class Demo {
/**
* 多线程技术
* 实现Runnable 与 继承Thread 相比有如下优势
* 1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务情况
* 2. 可以避免单继承带来的局限性
* 3. 任务与线程本身是分离的,提高了程序的健壮性
* 4. 后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
* @param args
*/
public static void main(String[] args) {
//线程:分为守护线程和用户线程
//用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
//守护线程:守护用户线程的,当最后一个用户线程金属时,所有守护线程自动死亡
Thread ti = new Thread(new MyRunnable());
//设置ti为守护线程,必须在ti开始之前设置
ti.setDaemon(true);
ti.start();
for (int i =1;i<5;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i =1;i<11;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("线程中断了,我们结束");
//return结束
return;
}
}
}
}
}
线程安全1-同步代码块
格式 synchronized(锁对象){
}
package task.kkb.tatk;
public class Demo {
/**
* 线程同步 :synchronized
* @param args
*/
public static void main(String[] args) {
//线程不安全
//解决方案1.同步代码块
//格式 synchronized(锁对象){
//
// }
Runnable run =new MyRunnable();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class MyRunnable implements Runnable{
private int pivket=10;
private Object o =new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (pivket > 0) {//买票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pivket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票为:" + pivket);
} else {
break;
}
}
}
}
}
}
线程安全2-同步方法
package task.kkb.tatk;
public class Demo {
/**
* 线程同步 :synchronized
* @param args
*/
public static void main(String[] args) {
//线程不安全
//解决方案1.同步代码块
//格式 synchronized(锁对象){
//
// }
Runnable run =new MyRunnable();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class MyRunnable implements Runnable{
private int pivket=10;
private Object o =new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (pivket > 0) {//买票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pivket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票为:" + pivket);
} else {
break;
}
}
}
}
}
}
显示锁lock和隐式锁synchronized的区别
一、层面不同
- synchronized:Java中的关键字,是由JVM来维护的,是JVM层面的锁。
- synchronized底层是通过monitorenter进行加锁
底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖于monitor对象的。并且只有在同步块或同步方法中,JVM才会调用monitory对象的,才可以调用wait/notify等方) - 通过monitorexit来退出锁
- synchronized底层是通过monitorenter进行加锁
- Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API,是API层面的锁。
- lock是通过调用对应的API方法来获取锁和释放锁的。
二、使用方式不同
- synchronized
程序能够自动获取锁和释放锁。Sync是由系统维护的,如果非逻辑问题的话话,不会出现死锁。 - Lock
需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。
手动获取锁方法:lock.lock()。释放锁:unlock方法。并且需要配合tyr/finaly语句块来完成。
三、等待是否可中断
-
synchronized
不可中断,除非抛出异常或者正常运行完成。 -
Lock
可以中断的。
中断方式:- 调用设置超时方法tryLock(long timeout ,timeUnit unit)
- 调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断
四、加锁的时候是否可以设置成公平锁
-
synchronized
只能为非公平锁。 -
lock:两者都可以的。默认是非公平锁。
在其构造方法的时候可以传入Boolean值。true:公平锁、false:非公平锁
五、锁绑定多个条件来condition
-
synchronized
不能精确唤醒线程。要么随机唤醒一个线程;要么是唤醒所有等待的线程。 -
Lock
用来实现分组唤醒需要唤醒的线程,可以精确的唤醒。
六、性能区别
- synchronized
托管给JVM执行,Java1.5中,由于需要调用操作接口,可能导致加锁消耗时间过长,与Lock性比性能低。1.6以后,语义定义更加清晰,有适应自旋、锁粗化、锁消除、轻量级锁、偏向锁等,可进行许多优化,性能提高了,与Lock差不多。 - Lock
java写的控制锁的代码,性能高。
注:
显示锁与隐式锁区别参考自:凯哥java
线程安全3-显示锁Lock
package task.kkb.tatk;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo2 {
/**
* 同步代码块 和同步方法 都属于隐式锁
* 同步线程:Lock
* @param args
*/
public static void main(String[] args) {
//解决方案3.显示锁 Lock 子类 ReentrantLock
Runnable run =new MyRunnable();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class MyRunnable implements Runnable{
//票数
private int pivket=10;
//显示锁 l
private Lock l =new ReentrantLock();
@Override
public void run() {
while (true) {
//买票
l.lock();
if (pivket > 0) {
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pivket--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票为:" + pivket);
}else {
break;
}
l.unlock();
}
}
}
}
公平锁与非公平锁
以上三种都是非公平锁,
公平锁:先来先得
非公平锁:同时抢
公平锁
//显示锁 l 加上fair参数为:true 就是公平锁 private Lock l =new ReentrantLock(true);
多线程通信问题
notifyAll()(唤醒其他所有线程)
wait()(该线程等待)
package task.kkb.tatk;
public class Demo5 {
public static void main(String[] args) {
Food f =new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师类
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i =1;i<101;i++){
if (i%2==0){
f.setNameAndSaste("佛跳墙","甜");
}else {
f.setNameAndSaste("朝天门","超辣");
}
}
}
}
//服务生
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for (int i =1;i<101;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
//true表示可以生产
private boolean flag =true;
public synchronized void setNameAndSaste(String name,String taste) {
if (flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
System.out.println("服务员端走的菜的名称是:"+name+",味道"+taste);
flag =true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 线程状态。 线程可以处于以下状态之一:
- NEW
尚未启动的线程处于此状态。 - RUNNABLE
在Java虚拟机中执行的线程处于此状态。 - BLOCKED
被阻塞等待监视器锁定的线程处于此状态。 - WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。 - TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。 - TERMINATED
已退出的线程处于此状态。
- NEW
带返回值的线程Callable
Runnable 与 Callable
接口定义
//Callable接口
public interface Callable { V call() throws Exception; }
//Runnable接口
public interface Runnable { public abstract void run(); }
使用步骤
1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable { @Override public call() throws Exception { return T; } }
2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask future = new FutureTask<>(callable);
3. 通过Thread,启动线程 new Thread(future).start()
package task.kkb.tatk;
import kotlin.reflect.KCallable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo6 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> c =new MyCallable();
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();
Integer j = task.get();
System.out.println("返回政治j为:"+j);
for (int i =0;i<10;i++){
System.out.println(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
//Thread.sleep(3000);
for (int i =0;i<10;i++){
Thread.sleep(100);
System.out.println(i);
}
return 100;
}
}
}
Runnable 与 Callable的相同点
都是接口 都可以编写多线程程序 都采用Thread.start()启动线程
Runnable 与 Callable的不同点
Runnable没有返回值;Callable可以返回执行结果 Callable接口的call()允许抛出异常;Runnable的run()不能抛出
Callable获取返回值 Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执 行,如果不调用不会阻塞。
缓存线程池
package task.kkb.tatk;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo7 {
/**
* 缓存线程池
* (长度无限制)
* 任务加入后执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 2. 不存在则创建线程并放入线程池,然后使用
* @param args
*/
public static void main(String[] args) {
//向线程池中加入新的任务
ExecutorService service = Executors.newCachedThreadPool();
//指挥线程执行新任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"123456");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"123456");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"123456");
}
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"123456");
}
});
}
}
定长线程池
package task.kkb.tatk;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo8 {
/**
* 定长线程池
* (长度是指定的数值)
* 任务加入后的执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况,则创建线程并放入线程池,然后使用
* 4. 不存在空闲线程,且线程池已满情况则等待线程池存在空闲
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"一二三四五");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"一二三四五");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"一二三四五");
}
});
}
}
单线程线程池
package task.kkb.tatk;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo9 {
/**
* 线程线程池
* 执行流程:
* 1. 判断线程池的那个线程是否空闲
* 2. 空闲则使用
* 3. 不空闲,则等待 池中的单个线程空闲后使用
* @param args
*/
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"一热三艘");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"一热三艘");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"一热三艘");
}
});
}
}
周期定长线程池
package task.kkb.tatk;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo0 {
/**
* 周期任务 定长线程池
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*
* 周期性任务执行时:
* 定时执行,当某个时机触发时,自动执行某任务.
* @param args
*/
public static void main(String[] args) {
ScheduledExecutorService service=Executors.newScheduledThreadPool(2);
/**
* 1. 定时执行一次
* 参数1. 定时执行的任务
* 参数2. 时长数字
* 参数3. 时长数字的时间单位,TimeUnit 的常量指定
*/
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("上山打老虎");
}
},5, TimeUnit.SECONDS);
/**
* 周期性执行任务
* 参数1. 任务
* 参数2. 延迟时长数字(第一次执行任务在多久以后)
* 参数3. 周期时长数字(没隔多久执行一次任务)
* 参数4. 时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("老虎有木有");
}
},5,1,TimeUnit.SECONDS);
}
}