public class MyThread extends Thread{
//run里面的逻辑就是线程要执行的工作,创建一个线程并安排执行的任务
@Override
public void run() {
System.out.println("hello thread");
}
//演示线程的基本创建
public static void main(String[] args) {
//程序中有两个线程 1、main主进程自带的线程(是JVM创建的,main方法是线程的入口)
//2、myThread创建的新线程(run方法是新线程的入口)
MyThread myThread = new MyThread();
//另外启动的一个线程,开始执行run方法,run方法结束后代表该线程结束
//该新线程是一个单独的执行流,与已有的主线程main是不干扰的,是并发(并发+并行)执行的
myThread.start();//只有调用start才代表开始启动线程,在操作系统内核中才会有一个与该线程对应
}
}
public class MyRunnable implements Runnable {
//runnable是用来表述线程要执行的具体操作,仅仅是把线程和线程要执行的工作分开,目的降低耦合度
@Override
public void run() {
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建一个runnable实例,runnable是描述具体的操作
MyRunnable myRunnable = new MyRunnable();
//创建一个线程,把runnable放进去
Thread thread = new Thread(myRunnable);
thread.start();
//多个线程执行同样的工作
Thread thread2 = new Thread(myRunnable);
thread2.start();
while (true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyThread3 {
//使用匿名内部类创建线程
public static void main(String[] args) {
//创建一个Thread的子类,同时实例化对象
Thread thread = new Thread(){
@Override
public void run() {
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
}
public class MyThread4 {
//使用匿名内部类实现Runnable接口
public static void main(String[] args) {
//匿名内部类的实例作为构造方法的参数,放在Thread的圆括号里面,创建一个线程实例
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}
}
public class MyThread5 {
//使用lambda表达式(本质上是一个匿名内部类)
public static void main(String[] args) {
//是MyThread的实现操作的间歇
Thread thread = new Thread(()->{
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
public class MyThread6 {
//给线程起名字->方便调试
public static void main(String[] args) {
//thread是变量名 只存在于代码中,而线程名既存在于代码中,又存在于执行的程序中
Thread thread = new Thread(()->{
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"MyThread777");
thread.start();
}
}
//线程等待(线程的调度顺序是未知的)
//通过join控制线程的之间的结束顺序(阻塞就是让线程不参与CPU的调度)
//join还可以设置为带时间的版本,表示等待几秒,如果超过了等待的时间,就不等了,就开始和main的线程并发执行了
//join不带时间的版本,就是一直等待,直到调用join方法的线程的逻辑执行完
public class MyThreadJoin {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
System.out.println("hello thread");
try {
//让线程休息,其实就是阻塞线程,不让线程参与CPU的调度
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
System.out.println("hello main 在join之前");
try {
//main线程进入阻塞不参与调度,而thread参与调度
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello main 在join之后");
}
}
public class MyThread7 {
//线程分为前台线程和后台线程
//关系是->只要有前台线程没有结束,程序就会一直进行,只要前台线程没有了,不管后台线程,程序直接结束
//线程默认是前台线程
public static void main(String[] args) {
Thread thread = new Thread(()->{
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"MyThread777");
//setDaemon把线程设置为后台线程
thread.setDaemon(true);
thread.start();
try {
//阻塞main线程,直到hello thread结束,在执行main线程
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main线程结束了");
//结果1是(没有使用join方法)->main线程结束了+hello thread,程序就结束了,因为main线程结束了,而thread是后台线程,(自己可以去掉join看结果)
//结果2是一直打赢hello thread,因为有join(),使用main线程要等到thread结束才能结束main线程
// 代表此时没有前台线程了,程序就结束
}
}
public class MyThread8 {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "MyThread888");
thread.start();
System.out.println(thread.getId());
System.out.println(thread.getName());
System.out.println(thread.getPriority());
System.out.println(thread.getState());
System.out.println(thread.isDaemon());
System.out.println(thread.isAlive());
System.out.println(thread.isInterrupted());
}
//12
//MyThread888
//5
//hello thread
//RUNNABLE
//false
//true
//false
}
class MyThread99 extends Thread {
//run里面的逻辑就是线程要执行的工作,创建一个线程并安排执行的任务
@Override
public void run() {
while(true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyThread9 {
public static void main(String[] args) {
MyThread99 myThread99 = new MyThread99();
//myThread99.start();//创建新线程,在新线程中执行代码,和main的线程并发执行 结果->hello thread和hello main"不规则"的出现
myThread99.run();//直接调run并没有创建线程,只是在main的线程中运行 结果->只出现hello thread(因为没有创建新线程,抢占了main线程的执行)
while(true) {
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//线程安全的基本使用
//1.使用synchronized修饰方法,进入方法加锁,方法执行完,自动解锁
class Counter{
private int count = 0;
public synchronized void increase(){
count++;
}
public int getCount(){
return count;
}
}
public class Demo {
private static Counter counter = new Counter();
public static void main(String[] args) throws InterruptedException {
//搞t1和t2线程,每个线程自增1w次,预期结果是2w
Thread thread1 = new Thread(()->{
for (int i = 0; i < 1_0000; i++) {
counter.increase();
}
});
Thread thread2 = new Thread(()->{
for (int i = 0; i < 1_0000; i++) {
counter.increase();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter.getCount());
}
//锁拥有独占特性,意思是当前没有人去加锁,尝试加锁的线程会成功
//但是如果当前锁有线程使用,其他线程要加锁,只能等待该锁解除
//synchronized会把并行变成串行,执行速度降低,但是准确性提高
//加锁不代表cpu会直接把线程t1执行完再去执行其他的,根据调度策略会切换,但是即使t1被调度走了,而锁依然没有释放,t2还是不能使用
//线程安全不是加锁就安全,而是通过加锁,让并发带来的问题操作变成串行操作
}
//increase加锁,increase2不加搜
class Counter2{
private static int count = 0;
public synchronized void increase(){
count++;
}
public void increase2(){
count++;
}
public int getCount(){
return count;
}
}
public class Demo2 {
private static Counter2 counter = new Counter2();
public static void main(String[] args) throws InterruptedException {
//搞t1和t2线程,每个线程自增1w次,预期结果是2w
Thread thread1 = new Thread(()->{
for (int i = 0; i < 1_0000; i++) {
counter.increase();
}
});
Thread thread2 = new Thread(()->{
for (int i = 0; i < 1_0000; i++) {
counter.increase2();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter.getCount());
}
//t1加锁,t2不加锁,不会出现锁竞争的情况,就不能把并行的问题的操作变成串行操作
}
//2.synchronized修饰代码块
class Counter3{
private static int count = 0;
public Object locker = new Object();
public void increase(){
//括号里要填入加锁的对象(任意对象)
//明确对哪个对象进行加锁,会影响是否触发阻塞(对象要根据使用场景而言)
//一个synchronized只能锁一个对象
synchronized(this){
//锁this等价于锁方法
//针对当前对象进行加锁,谁调用increase方法谁就是当前锁对象
count++;
}
// synchronized(locker){
// //针对locker加锁,如果创建的对象中有一样的locker实例,那就是针对同一对象加锁(产生锁竞争)
// count++;
// }
}
public int getCount(){
return count;
}
}
public class Demo3 {
private static Counter3 counter = new Counter3();
public static void main(String[] args) throws InterruptedException {
//搞t1和t2线程,每个线程自增1w次,预期结果是2w
Thread thread1 = new Thread(()->{
for (int i = 0; i < 1_0000; i++) {
counter.increase();
}
});
Thread thread2 = new Thread(()->{
for (int i = 0; i < 1_0000; i++) {
counter.increase();
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter.getCount());
}
//两个线程锁必须同一个对象,才会产生锁竞争现象
}
b.使用volatile关键字
import java.util.Scanner;
//volatile保证内存可见性,不保证原子性
public class Demo9 {
static class Counter{
//volatile只能去修饰变量,无法修饰其他的东西
public volatile int count = 0;
//volatile修饰的变量会让编译器不去优化改变量为只读寄存器而不读内存
}
public static void main(String[] args) {
int a = 1;
Demo9.Counter counter = new Demo9.Counter();
Thread thread = new Thread(()->{
while(counter.count == 0){
}
System.out.println("t1执行结束");
});
thread.start();
Thread thread2 = new Thread(()->{
System.out.println("请输入一个整数:");
Scanner scanner = new Scanner(System.in);
counter.count = scanner.nextInt();
});
thread2.start();
}
//死循环的原因:t2线程的写操作对t1的读操作不会有影响,因为t1的操作已经被优化成了读1次count的内容就不在读了(内存可见性问题,没有加volatile)
//内存可见性:在编译器的优化下(因为count的值是不变的,编译器就优化了),一个线程把内存的值改了,因为优化的原因,另一个线程无法感知到改值已修改
//编译器的优化不是一直优化的,要取决于当前的环境因素(可以给代码加上sleep去看)
}