1.什么是线程
1.1程序
程序是指为了完成某一套特定的任务而编写的具体文件
1.2进程
进程是程序的一次执行过程,是系统运行的基本单位。每个进程占有某些系统资源(CPU、内存等)
1.3线程
线程与进程类似,线程是比进程更小的执行单位。一个进程在其执行的过程中,产生多个线程。多个线程是共享同一块内存空间和一组系统资源,所以多个线程相互切换工作时,负担比进程小的多,线程也被称为轻量级进程。
2.如何创建线程
2.1通过继承Thread来重写run方法
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run==="+i);
}
}
}
调用start方法启动新线程
注意:如果直接调用run()方法,不会创建一个新线程,而只会在当前线程中执行run方法的内容
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();//创建线程对象
myThread.start();//启动线程方法
for (int i = 0; i < 10; i++) {
System.out.println("main============"+i);
}
}
}
运行结果:
main============0
main============1
main============2
main============3
run===0
run===1
run===2
run===3
run===4
run===5
main============4
run===6
main============5
run===7
run===8
run===9
main============6
main============7
main============8
main============9
Process finished with exit code 0
2.2通过实现Runnable来重写run方法(推荐)
避免了单继承的限制,可以扩展业务
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run====="+i);
}
}
}
创建方式与上面一样都调用.start()方法来创建一个新线程。但注意的是实现Runnable来使用时,要创建一个线程,再将这个实例传给这个线程
public class TestRunnable {
public static void main(String[] args){
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);//创建线程传递myRunnable
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("main============"+i);
}
}
}
3.线程的主要方法
3.1获取设置当前线程名称
getName()获取当前线程名称
setName()设置当前线程名称
public class TestRunnable {
public static void main(String[] args){
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);//创建线程传递myRunnable
thread.start();//启动线程
thread.setName("run线程");//设置线程name
Thread.currentThread().setName("主线程");//设置主线程name
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"============"+i);//获取主线程name
}
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"====="+i);//获取run线程name
}
}
}
3.2休眠
sleep(),这个方法用来让当前线程休眠指定的毫秒数
public class MyRunnable implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000);//休眠一秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"====="+i);//获取run线程name
}
}
}
3.3yield
让出当前进程时间片,使其他相同优先级的线程有机会执行,这个会尽量平均分配cpu资源
public class MyRunnable implements Runnable{
@Override
public void run() {
Thread.yield();//放弃cpu资源,允许其他线程访问
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"====="+i);//获取run线程name
}
}
}
3.4join
.join()可以使一个线程等待另一个线程结束
public class TestRunnable {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);//创建线程传递myRunnable
thread.start();//启动线程
thread.setName("run线程");//设置线程name
Thread.currentThread().setName("主线程");//设置主线程name
thread.join(); //主线程会等run线程结束
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"============"+i);//获取主线程name
}
}
}
3.5优先级
setPriority(1-10)用于设置优先级,数字越高优先级越高,更有可能被调用。
public class TestRunnable {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable,"线程1");//创建线程传递myRunnable
Thread thread2 = new Thread(myRunnable,"线程2");
Thread thread3 = new Thread(myRunnable,"线程3");
thread3.setPriority(10);
thread2.setPriority(5);
thread1.setPriority(1);
thread1.start();//启动线程
thread2.start();
thread3.start();
}
}
会发现优先级高的被调用的可能性高一点
4.守护线程
通过调用setDaemon(true)来实现守护线程,当用户线程执行完毕后,守护线程也会相应结束
public class TestRunnable {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable,"线程1");//创建线程传递myRunnable
thread1.setDaemon(true);//设置为守护线程
thread1.start();//启动线程
for (int i = 0; i < 1; i++) {
System.out.println(Thread.currentThread().getName()+"====="+i);
}
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"====="+i);//获取run线程name
}
}
}
这个主线程执行一次,线程1执行二十次,但主线程一结束,线程二也会相应结束
5.线程安全问题
多个线程同时修改一个变量
案例1:四个窗口共卖100张票
public class MyRunnable implements Runnable{
private static int ticket=100;
@Override
public void run() {
synchronized (this){
while (true){
if (ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+ticket);
}else {
break;
}
}
}
}
}
public class TestRunnable {
public static void main(String[] args){
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable,"A窗口");//创建线程传递myRunnable
Thread thread2 = new Thread(myRunnable,"B窗口");//创建线程传递myRunnable
Thread thread3 = new Thread(myRunnable,"C窗口");//创建线程传递myRunnable
Thread thread4 = new Thread(myRunnable,"D窗口");//创建线程传递myRunnable
thread1.start();//启动线程
thread2.start();//启动线程
thread3.start();//启动线程
thread4.start();//启动线程
}
}
运行几次发现问题,有重卖和超卖的现象,这里就是线程安全问题造成的
案例2: A线程将“Hello”存入数组;B线程将“World”存入数组。
public class TestRun {
private static String[] arr=new String[2];
private static int index=0;
public static void main(String[] args) throws InterruptedException {
Thread t01= new Thread(new Runnable() {
@Override
public void run() {
if (arr[index]==null){
arr[index]="hello";
index++;
}
}
});
Thread t02= new Thread(new Runnable() {
@Override
public void run() {
if (arr[index]==null){
arr[index]="wold";
index++;
}
}
});
t01.start();
t02.start();
t01.join();
t02.join();
System.out.println(Arrays.toString(arr));
}
}
四种结果
a.[hello,null] b.[hello,world] c.[world,hello] d.[world,null]
6.锁
上面的线程安全问题我们可以用到锁,凡是用锁,操作的代码都是原子性
6.1自动锁
synchroized
可以修饰代码块、静态方法、普通方法
public class MyRunnable implements Runnable {
private static int ticket = 100;
@Override
public void run() {
aaa();
}
public synchronized void aaa(){ //修饰方法
while (true) {
if (ticket > 0) {
ticket--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket);
} else {
break;
}
}
}
}
也可以synchroized(Object object){
}
object为同一个object对象
public class MyRunnable implements Runnable {
private static int ticket = 100;
private Object aa = new Object();
@Override
public void run() {
while (true) {
synchronized (aa) {
if (ticket > 0) {
ticket--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket);
} else {
break;
}
}
}
}
}
解决了多卖和重卖的问题
6.2手动锁
lock只能用在代码块
创建Lock实例
Lock lock=new ReetrantLock();
加锁
lock.lock();
解锁
lock.unlock();
public class MyRunnable implements Runnable {
private static int ticket = 100;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();//加锁
if (ticket > 0) {
ticket--;
System.out.println(Thread.currentThread().getName() + "卖了一张票,还剩" + ticket);
} else {
break;
}
lock.unlock();//解锁
}
}
}