六.Java 线程、并发

6.1 线程

6.1.1 原理
(1)创建线程
线程必须有一个名字,否则会抛出异常:
  if (name == null) {
      throw new NullPointerException("name cannot be null");
  }

平时用Thread()创建线程的时候也没有指定名字,为什么没有报错?那是因为java默认会指定一个名字。
	public Thread() {
  	init(null, null, "Thread-" + nextThreadNum(), 0);
  }


默认的名字就是"Thread-" + nextThreadNum(),
创建几个线程,然后打印一下名字:
线程1:Thread-0
线程2:Thread-1
...


(2)执行线程
public synchronized void start()

synchronized关键字表示此方法是同步的,同一时间内,只能有一个代码来调用start()
默认threadStatus = 0
  if (threadStatus != 0)
  throw new IllegalThreadStateException();
!不能多次启动一个线程。会报出异常:
Exception in thread "main" java.lang.IllegalThreadStateException




(3)休眠
以指定的毫秒休眠
	public static void sleep(long millis)
if (millis < 0) {
  throw new IllegalArgumentException("timeout value is negative");
  }
其中millis不能小于0,负责抛出异常:
Exception in thread "Thread-0" java.lang.IllegalArgumentException: timeout value is negative


(4)终止线程
	public class Test extends Thread
	{
		@Override
		public void run()
		{
			while (true)
			{
				try
				{
					System.out.println("running...");
					Thread.sleep(1000);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
				
				System.out.println("stop");
			}
		}
	}


然后调用:
	public static void main(String[] args)
	{
		Test test1 = new Test();
		test1.start();
		for(int i=1;i<1000000;i++){
		}
		test1.stop();
	}

会发现只是输出了running...,而终止的时候并没有输出stop,
API中已不建议使用stop()来终止线程,如果线程中有其他操作,突然终止线程的话,会带来一些意外的结果。


安全终止线程的方法:通过标记
public class Test extends Thread
{
	public  boolean	flag	= true;


	@Override
	public void run()
	{
		while (flag)
		{
			try
			{
				System.out.println("running...");
				Thread.sleep(1000);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}


			System.out.println("stop");
		}
	}


	public void setFlag(boolean flag)
	{
		this.flag = flag;
	}
}

终止线程:setFlag(false);




6.1.2 多线程的执行顺序

	Test test1 = new Test("1");
	test1.start();
	
	Test test2 = new Test("2");
	test2.start();


输出:
running...1
running...2


running...2
running...1


running...2
running...1
可以看到两个线程的执行顺序不是固定不变的,执行哪个线程是由JVM决定的。可以通过setPriority来影响执行顺序。


测试:
	Test test1 = new Test("1");
	test1.start();
	Test test2 = new Test("2");
	test2.setPriority(Thread.MIN_PRIORITY);
	test2.start();
	Test test3 = new Test("3");
	test3.start();


输出:
...
running...2
running...3
running...1
...


!在单核CPU上多任务的执行其实是轮流执行,在一个时间点上,CPU只能执行一个任务,比如一边看电影,一边听歌,CPU加载一点电影,然后去加载一点歌曲,如此反复,
因为每次执行的时间很短,就造成了同步执行任务的假象。
以windows为例在操作系统级别,每个线程最大运行时间大概是20ms,也就是操作系统的时间片是20ms,线程根据时间片来运行。假如到了20ms,cpu将切换到下一个线程执行。
当再次切换回来的时候,cpu按照上次执行的进度继续执行该线程。


6.2 进程


进程是程序的执行过程,从加载到执行,最后结束的一个完整的过程。
线程是让代码并发执行的一个手段。
比如在windows任务管理器中可以看到有很多进程,有时一个软件有很多进程,
例如Chrome浏览器就有很多进程,包含了软件的核心进程和页面进程。每打开一个新网页,就会开辟一个进程用于次网页。
当一个网页的进程阻塞或者崩溃后,不会影响其它进程。
这和线程是一个道理。一些耗时的操作,一般不会在主线程中运行,而是新开一个线程运行,这样就不会阻塞主线程。



6.3 并发

多个线程同时访问一个资源产生并发。


假如8888的账户有100块,A和B两个人同时在2个取款机上取钱:
服务器:A要在8888账户取100,余额够么?
数据库:够。
服务器:B要在8888账户取100,余额够么?
数据库:够。
服务器:A取走了,你改下数据。
数据库:OK。
服务器:B取走了,你改下数据。
数据库:A刚才不是把8888取光了么,你个傻B。
服务器:额。。。


解决这个问题的方法就是加锁。A操作的时候锁住这个账户的数据,等A的操作,取钱,存钱,等,修改完数据库以后,B的操作再进行,
如果A取完了账户中的钱,那么给B显示余额不足。
加锁在java中用synchronized关键字。

public class Main
{
	public static void main(String[] args)
	{
		Test1 t1 = new Test1();
		Test1 t2 = new Test1();
		t1.start();
		t2.start();
	}


	static class Test1 extends Thread
	{
		@Override
		public void run()
		{
			while (true)
			{
				try
				{
					//synchronized (this)
					{
						System.out.println("running 1");
						System.out.println("running 2");
						System.out.println("running 3");
					}
					
					Thread.sleep(100);
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}
	}
}


结果:
running 1
running 1
running 2


running 2
running 3
running 3


running 1
running 2
running 3
...
没有加锁的情况下,每个线程都可以随时访问该代码块。
加锁以后,结果:
running 1
running 2
running 3


running 1
running 2
running 3


running 1
running 2
running 3
...
同一个时间只有一个线程可以访问该代码块。别的线程想访问就必须排队。













评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值