java-线程
程序
是为了完成特定任务,用某种语言编写的一组指令的集合
进程
进程是指运行中的程序。进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程,有他自身的产生,存在和消亡过程。
线程
线程是由进程创建的,是进程的一个实体。一个进程可以拥有多种线程
- 单线程: 同一个时刻,只允许执行一个线程
- 多线程: 同一个时刻,可以执行多个线程。
- 并发: 同一个时刻,多个任务交替执行。(单核cpu实现的多任务就是并发)
- 并行: 同一个时刻,多个任务同时执行,多核cpu可以实现并行。
创建线程
继承Thread类 创建线程
package com.xxb.threaduse;
public class Thread01 {
public static void main(String[] args) {
//创建Cat对象,可以当做线程
Cat cat = new Cat();
//启动线程
cat.start();
//说明:当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行。
System.out.println("主线程继续执行"+Thread.currentThread().getName());
for (int i = 0; i <60 ; i++) {
System.out.println("主线程 i="+i);
//让主线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//1.当一个类继承了Thread类 该类就可以当线程使用
//2. 我们会重写run方法,写上自己的业务代码
//3. run Thread类实现的Runnable接口的run方法
class Cat extends Thread {
int times=0;
//重写run方法 写上自己的业务逻辑
@Override
public void run() {
while (true) {
//该线程每隔1s,在控制台输出"喵喵,我是小猫咪"
System.out.println("喵喵,我是小猫咪"+(++times)+"线程名="+Thread.currentThread().getName());
//让该线程休眠1s ctrl+alt+t
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(times==80){
break;
}
}
}
}
Runnable创建线程(使用Thread.proxy)
package com.xxb.threaduse;
public class Thread02 {
public static void main(String[] args) {
Dog dog=new Dog();
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{
int count=0;
@Override
public void run(){
while(true){
System.out.println("小狗汪汪叫"+(++count)+Thread.currentThread().getName());
//休眠1s
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==10){
break;
}
}
}
}
多线程执行
package com.xxb.threaduse;
/**
* main线程启动2个子线程
*/
public class Thread03 {
public static void main(String[] args) {
T1 t1 = new T1();
T2 t2 = new T2();
//开启两个子线程
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
}
class T1 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("hello world " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==20){
break;
}
}
}
}
class T2 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
System.out.println("hi " + (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(count==10){
break;
}
}
}
}
继承Thread 与实现Runnable的区别
案例:多线程售票(会出现超卖问题)
package com.xxb.ticket;
/**
* 使用多线程,模拟三个窗口同时售票
*/
public class Sellticket {
public static void main(String[] args) {
//测试
// SellTicket01 sellTicket01 = new SellTicket01();
// SellTicket01 sellTicket02 = new SellTicket01();
// SellTicket01 sellTicket03 = new SellTicket01();
//这里会出现超卖的问题。
// sellTicket01.run();
// sellTicket02.run();
// sellTicket03.run();
//Runable 方式
SellTicket02 sellTicket02 = new SellTicket02();
//这里也会出现超卖的问题
new Thread(sellTicket02).start();
new Thread(sellTicket02).start();
new Thread(sellTicket02).start();
}
}
//使用Thread方式
class SellTicket01 extends Thread{
//让多个线程共享 ticketNum
private static int ticketNum=2;
@Override
public void run(){
while (true){
if(ticketNum<=0){
System.out.println("售票结束");
break;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));
}
}
}
class SellTicket02 implements Runnable{
//让多个线程共享 ticketNum
private int ticketNum=100;
@Override
public void run(){
while (true){
if(ticketNum<=0){
System.out.println("售票结束");
break;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));
}
}
}
线程终止(通知线程退出)
- 当线程完成任务后,会自动退出
- 还可以通过使用变量来控制run方法退出的方式停止线程。
package com.xxb.exit;
public class ThreadExit_ {
public static void main(String[] args) {
T t1 = new T();
t1.start();
//如果希望main线程去控制t1线程的终止,必须可以修改loop
//让t1退出run方法,从而终止t1线程
//让主线程休眠10s,再通知t1线程退出
try {
Thread.sleep(10*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.setLoop(false);
}
}
class T extends Thread{
private int count=0;
//设置一个控制变量
private boolean loop=true;
@Override
public void run(){
while(loop){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("AThread 运行中..."+ (++count));
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
线程常用方法
线程中断
package com.xxb.method;
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.setName("cnm");
//最低优先级
t.setPriority(Thread.MIN_PRIORITY);
System.out.println(t.getName());
//启动线程
t.start();
//主线程打印5 句hi 然后我就中断 子线程的休眠
for(int i=0;i<5;i++){
Thread.sleep(1000);
System.out.println("hi "+i);
}
System.out.println(t.getName()+" 线程的优先级=" +t.getPriority());
//中断线程
t.interrupt();
}
}
class T extends Thread{
@Override
public void run() {
while (true) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 吃包子。。。" + i);
}
System.out.println(Thread.currentThread().getName() + " 休眠中。。。");
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
//当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
//InterruptedException 是捕获到一个中断异常
System.out.println(Thread.currentThread().getName() + "被 interrupet了");
}
}
}
}
线程插队
package com.xxb.method;
public class ThreadMethod02{
public static void main(String[] args) throws InterruptedException {
T2 t2 = new T2();
t2.setName("cnm");
//启动线程
t2.start();
for(int i=0;i<20;i++){
Thread.sleep(1000);
System.out.println("主线程(小弟)吃包子 "+i);
if(i==5){
System.out.println("主线程(小弟)让子线程(老大)先吃");
//这里相当于t2线程先执行 让t2插队
t2.join();
//礼让 不一定成功
// Thread.yield();
System.out.println("子线程(老大)吃完了 主线程(小弟)接着吃");
}
}
}
}
class T2 extends Thread{
@Override
public void run() {
// while (true) {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 子线程吃包子。。。" + i);
}
// }
}
}
用户线程和守护线程
package com.xxb.method;
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
//守护线程
MyDaemonThread myDaemonThread = new MyDaemonThread();
//将子线程设置守护线程即可
myDaemonThread.setDaemon(true);
//开启线程
myDaemonThread.start();
for(int i=1;i<=10;i++){
System.out.println("fck");
Thread.sleep(1000);
}
}
}
//守护线程
class MyDaemonThread extends Thread{
@Override
public void run(){
for(;;){//无限循环
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("不抱怨,少说话,多做事");
}
}
}
线程的生命周期
线程的同步机制(Synchronized)
- 在多线程编程,一些敏感数据不允许别多个线程同时访问,此时就使用同步访问技术,保证数据再任何同一时刻,最多有一个线程访问,以保证数据的完整性。
- 也可以这样理解: 线程同步,即当有一个线性在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
同步的具体方法
用同步方法解决卖票超卖问题。
package com.xxb.sync;
/**
* 使用多线程,模拟三个窗口同时售票
*/
public class Sellticket {
public static void main(String[] args) {
//测试
// SellTicket01 sellTicket01 = new SellTicket01();
// SellTicket01 sellTicket02 = new SellTicket01();
// SellTicket01 sellTicket03 = new SellTicket01();
//这里会出现超卖的问题。
// sellTicket01.run();
// sellTicket02.run();
// sellTicket03.run();
//Runable 方式
// SellTicket02 sellTicket02 = new SellTicket02();
// //这里也会出现超卖的问题
// new Thread(sellTicket02).start();
// new Thread(sellTicket02).start();
// new Thread(sellTicket02).start();
// 同步
SellTicket03 sellTicket03 = new SellTicket03();
//这里也会出现超卖的问题
new Thread(sellTicket03).start();
new Thread(sellTicket03).start();
new Thread(sellTicket03).start();
}
}
//使用Thread方式
class SellTicket01 extends Thread{
//让多个线程共享 ticketNum
private static int ticketNum=2;
@Override
public void run(){
while (true){
if(ticketNum<=0){
System.out.println("售票结束");
break;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));
}
}
}
class SellTicket02 implements Runnable{
//让多个线程共享 ticketNum
private int ticketNum=100;
@Override
public void run(){
while (true){
if(ticketNum<=0){
System.out.println("售票结束");
break;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));
}
}
}
//实现接口方式, 使用synchronized实现线程同步
class SellTicket03 implements Runnable{
//让多个线程共享 ticketNum
private int ticketNum=100;
//控制变量
private boolean loop=true;
//同步方法,在同一时刻,只有一个线程来执行run方法
public synchronized void sell(){
if(ticketNum<=0){
System.out.println("售票结束");
loop=false;
return;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));
}
@Override
public void run(){
while (loop){
this.sell();
}
}
}
互斥锁
线程执行图
锁在方法上
//锁在方法上
public synchronized void sell(){
if(ticketNum<=0){
System.out.println("售票结束");
loop=false;
return;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票"+" 剩余票数="+(--ticketNum));
}
同步代码块
public /*synchronized*/ void sell() {
//同步代码块
synchronized (this) {
if (ticketNum <= 0) {
System.out.println("售票结束");
loop = false;
return;
}
//休眠50ms
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
}
}
//静态方法的锁为当前类本身 即锁是加在SellTicket03.class上。
public synchronized static void m1(){
}
//在静态方法中,实现一个同步代码块
public static void m2(){
synchronized (SellTicket03.class){
System.out.println("m2");
}
}
互斥锁注意事项
线程的死锁
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁。