QQ
,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用其他程序的时候,就又启动了一个进程,操作系统就会为其他进程分配新的内存空间下面就是几个进程
同一时刻,可以执行多个进程,比如上面的迅雷同时下载多个文件
同一时刻,只允许执行一个线程
并发 :
同一时刻 , 多个任务交替执行, 造成一种‘貌似同时’ 的错觉, 简单的说,单核cpu
实现的多任务就是并发
看上去他们同时进行,但是在任意时间段内,他们都是相互交错执行的
比如 : ‘人的大脑一边说话,一边写字 ’
同一时刻, 多个任务同时执行。多核cpu
可以实现并行
public class Thread01 {
public static void main(String[] args) {
//1. 创建一个cat对象,可以当作一个线程来使用
Cat cat = new Cat();
cat.start(); //启动线程 - > 最终会执行cat的run方法
}
}
class Cat extends Thread{
int times = 0;
//todo 通过重写run方法,来实现自己的业务逻辑
@Override
public void run() { // 实现了Runnable接口的run方法
while(true){
System.out.println("执行一次" + "次数" +(++times));
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果就是每隔一秒输出一次
java
是单继承的,在某些情况下一个类可能已经继承了某个父类,这是再用Thread类方法来创建线程显然是不可能了java
设计者们就想出了另一个创建线程的方法,就是通过实现Runnable接口来创建线程写一个程序,该程序可以每隔1秒,在控制台输出一段话
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//无法调用start方法 ,切记不能调用run方法,因为他只是一个普通的方法,没办法开启线程
Thread thread = new Thread(dog);
//这时 ,我们可以创建一个Thread对象, 然后调用start方法 ,然后就可以启动一个线程,调用了run方法
thread.start();
}
}
class Dog implements Runnable{//通过Runnable接口实现线程
int times = 0;
@Override
public void run() {
System.out.println("执行一次" + "次数" +(++times)+ "线程名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这里使用的是一个设计模式【代理模式】
当我们执行上述程序是,它的流程是这样的
主线程的执行
public static void main(String[] args) throws Exception{
int count = 0;
//1. 创建一个cat对象,可以当作一个线程来使用
Cat cat = new Cat();
cat.start();
//当main方法执行一个线程 之后 ,主线程不会阻塞 ,会继续执行
while (true){
System.out.println("主线程 执行次数: " + (++count) + "线程名:" + Thread.currentThread().getName());
Thread.sleep(1000);
}
}
当main方法执行一个线程 之后 ,主线程不会阻塞 ,会继续执行
最后用检测工具发现,虽然主线程的执行完一定次数之后,结束了,但是它的子线程Thread-0 没有执行完 ,那么程序就不会结束
cat.start()
方法?答 :因为run方法就是一个普通的方法,并无法启动一个真正的线程就会产生阻塞。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//本地方法, 是由jvm调用,底层是由c/c++实现,无法调用
private native void start0();
换言之, 真正实现多线程的效果是start0();
方法
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
Catt catt = new Catt();
//无法调用start方法 ,切记不能调用run方法,因为他只是一个普通的方法,没办法开启线程
Thread thread1 = new Thread(dog);
Thread thread2 = new Thread(catt);
//这时 ,我们可以创建一个Thread对象, 然后调用start方法 ,然后就可以启动一个线程,调用了run方法
thread1.start();
thread2.start();
}
}
class Dog implements Runnable{//通过Runnable接口实现线程
int times = 0;
@Override
public void run() {
while (true){
System.out.println("执行一次" + "次数" +(++times)+ " 线程1名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Catt implements Runnable{//通过Runnable接口实现线程
int times = 0;
@Override
public void run() {
while (true){
System.out.println("执行一次" + "次数" +(++times)+ " 线程2 名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果 ,他们交叉执行
package ticket;
public class SellTicket {
public static void main(String[] args) {
SellTicket01 ticket01 = new SellTicket01();
SellTicket01 ticket02 = new SellTicket01();
SellTicket01 ticket03 = new SellTicket01();
ticket01.start();
ticket02.start();
ticket03.start();
}
}
class SellTicket01 extends Thread{
static int ticketNum = 100;
@Override
public void run() {
while (true){
if (ticketNum <= 0 ){
System.out.println("售票结束");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口 :" + Thread.currentThread().getName() + "售出一张票" + " 剩余票数为: "+ (--ticketNum));
}
}
}
运行结果 :
出现超卖问题
原因:
也就是在票数为 1 的时候, 他们三者同时进行售卖 ,导致三个线程同时进行,然后就会超卖两张
解决方案:
public class Thread01 {
public static void main(String[] args) throws Exception{
int count = 0;
//1. 创建一个cat对象,可以当作一个线程来使用
Cat cat = new Cat();
cat.start();
//休息10 秒 让其退出
Thread.sleep(10000);
cat.loop = false;
//当main方法执行一个线程 之后 ,主线程不会阻塞 ,会继续执行
}
}
class Cat extends Thread{
int times = 0;
boolean loop = true;
//todo 通过重写run方法,来实现自己的业务逻辑
@Override
public void run() { // 实现了Runnable接口的run方法
while(loop){
System.out.println("执行一次" + "次数" +(++times)+ "线程名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在main方法中控制变量,让另一个线程退出
运行结果:
就是让正在休眠的线程停止休眠, 继续工作的意思
package Thread;
public class Thread01 {
public static void main(String[] args) throws Exception{
int count = 0;
//1. 创建一个cat对象,可以当作一个线程来使用
Cat cat = new Cat();
cat.start();
//休息10 秒 让其退出
Thread.sleep(1000);
// cat.loop = false;
System.out.println("线程停止休眠 ,继续执行" );
cat.interrupt(); //中断子线程的休眠
//Thread.sleep(5000);
//当main方法执行一个线程 之后 ,主线程不会阻塞 ,会继续执行
}
}
class Cat extends Thread{
int times = 0;
boolean loop = true;
//todo 通过重写run方法,来实现自己的业务逻辑
@Override
public void run() { // 实现了Runnable接口的run方法
while(loop){
System.out.println("执行一次" + "次数" +(++times)+ "线程名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
System.out.println("休眠中--");
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被interrupt了");
}
}
}
}
public class Thread02 {
public static void main(String[] args) throws Exception{
Dog dog = new Dog();
Catt catt = new Catt();
Thread thread1 = new Thread(dog);
//Runnable方式实现 设置为守护线程 ,然后在启动
thread1.setDaemon(true);
thread1.start();
//Thread 实现 设置为守护线程 ,然后在启动
catt.setDaemon(true);
catt.start();
for (int i = 0; i < 10; i++) {
System.out.println("test... ");
Thread.sleep(1000);
}
}
}
class Dog implements Runnable{//通过Runnable接口实现线程
int times = 0;
@Override
public void run() {
while (true){
System.out.println("执行一次" + "次数" +(++times)+ " 线程1名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Catt extends Thread{//通过Runnable接口实现线程
int times = 0;
@Override
public void run() {
while (true){
System.out.println("执行一次" + "次数" +(++times)+ " 线程2 名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
通过//Runnable方式实现 设置为守护线程 ,然后在启动 thread1.setDaemon(true);
的方式实现设置守护线程
注意顺序 ,先设置 再执行
jdk官方文档中可以看出有六个
但是从我们的线程生命周期转换图可以看出 我们将Runnable(可运行状态)细化为Ready(就绪) 和Running(真正运行)两个状态
public class Thread02 {
public static void main(String[] args) throws Exception{
Catt catt = new Catt();
//Thread 实现 设置为守护线程 ,然后在启动
System.out.println(catt.getName() + " 状态 " + catt.getState());
catt.start();
while(Thread.State.TERMINATED != catt.getState()){
System.out.println(catt.getName() + " 状态 " + catt.getState());
Thread.sleep(500);
}
System.out.println(catt.getName() + " 状态 " + catt.getState());
}
}
class Catt extends Thread{//通过Runnable接口实现线程
int times = 0;
@Override
public void run() {
while (times != 5){
System.out.println("执行一次" + "次数" +(++times)+ " 线程2 名:" + Thread.currentThread().getName());
//让其休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果:
Synchronized
同步具体方法
//得到对象的锁,才能操作同步代码
synchronized(对象){
//需要被同步的代码
}
public synchronized void m(String name){
//需要被同步的代码
}
具体理解 ,不管有多少线程来,在某一时刻,只能有一个线程访问
假设 t1 抢到锁之后就开始执行代码 ,当执行完之后就会把锁放回去 ,然后t1、t2、t3 三个继续抢这个锁
**注意事项: **
上述情况导致o1、o2都无法释放,导致死锁
必须避免这种两个锁互相锁