Java高并发入门-线程初步
线程与进程之间的关系
进程就是我们运行在计算机上的一个程序,对应Java程序来说就是运行在计算机上的Java应用程序,这个程序在运行的时候就会创建了一个进程,服务器上就会存在一个Java进程。而对于线程来讲,线程是运行在进程里面。为了执行一条具体任务。比如说一个人同时可以做两件时间,同时听音乐或者看电视等操作。单线程的程序来说就只能等到某个线程执行完毕之后才会执行下一个线程任务。不会存在多个线程之间竞争的问题,但是这样的单线程的执行效率比较低。
Java 实现多线程的方式有几种
- 实现Runnable接口
- 继承Thread类
- 实现Callable接口
- 实现Runnable接口的方式
private static class TestDemo implements Runnable{
private int count = 1;
@Override
public void run() {
System.out.println(count++);
}
}
public static void main(String[] args) {
TestDemo testDemo = new TestDemo();
Thread thread = new Thread(testDemo);
Thread thread1 = new Thread(testDemo);
thread.start();
thread1.start();
testDemo.run();
//count的值
//1 2 2 1 2 2 1 1
}
同这个例子可以判断一下count的值是多少。这里也为后面锁竞争问题的关键点。
蓝色代表我们主线程,红色代表创建两个线程,由于Count是一个共享资源所以在访问的时候会出现锁竞争的问题,如图所示,主线程直接执行,而两个线程由于要进行锁竞争,会出现第一个线程先抢占Count资源然后进行加一操作,第二个线程抢占资源之后就是加一后的值。
-继承Thread类
private static class Demo extends Thread{
private int count = 1;
@Override
public void run() {
System.out.println(count++);
}
}
public static void main(String[] args) {
new Demo().start();
new Demo().start();
System.out.println("==========******==============");
Demo demo = new Demo();
demo.start();
System.out.println("=======================");
Demo demo1 = new Demo();
Thread thread = new Thread(demo1);
thread.start();
}
分析代码
- 前两组是一个结果,中间是一个结果,最后是一组结果,三组结果有什么区别
- 这个就引出一个问题,锁竞争问题
- 第一组结果
这个结果是怎么样输出的,首先因为Demo继承Thread所以说在输出的时候,肯定是实现了toString,由于Demo没有toString方法所以说只能在其父类中找到
public String toString() {
ThreadGroup group = getThreadGroup();
if (group != null) {
return "Thread[" + getName() + "," + getPriority() + "," +
group.getName() + "]";
} else {
return "Thread[" + getName() + "," + getPriority() + "," +
"" + "]";
}
}
从这个方法中可以看到首先获取线程组,判断线程组是否为空,然后获取到线程组的名称,获取到线程组的运行级别进行输入。到这里可以发现两个结果,这个两个结果的线程名称不是同一个,运行级别相同,同属于Main线程组。
因为Demo继承了Thread类所以说与Thread类有相同方法。所以说可以当做一个线程来创建。为什么没有出现锁竞争问题,是因为,在这里我们创建两个不一样的线程,访问的两个不一样的资源,所以说没有出现锁竞争问题。如果在程序运行过程中,出现访问共享资源的情况就会出现锁竞争问题。
从图中可以看到这个红色代表两个匿名对象,而蓝色代表主线程。就是这样的一个关系图,由于两个匿名对象不是同一个对象,所以说没有没有出现锁竞争问题,第二组是与第一组类似而第三组
Demo demo1 = new Demo();
Thread thread = new Thread(demo1);
thread.start();
这个方法看上去是比较繁琐的,本来可以直接启动的,却使用了新的Thread对象来进行线程的创建,而这三种方式有什么区别。为了判断这三种方式的正确性我们分析一下Thread的源码。
API推荐方式
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
PrimeThread p = new PrimeThread(143);
p.start();
这个创建与使用与上面我们的创建与使用是类似。
进入Thread源码首先是一个无参构造函数调用init方法
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
if (security != null) {
g = security.getThreadGroup();
}
if (g == null) {
g = parent.getThreadGroup();
}
}
g.checkAccess();
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
tid = nextThreadID();
}
什么是锁竞争?
在多线程编程过程作用,会遇到多个线程访问同一个资源的情况,这个资源有可能是方法,类,成员变量等等,这个时候就会出现多个线程同时要去使用这个资源才能够继续运行,就会出现过个线程争抢同一个资源的情况。把这种情况就可以称为锁竞争。